package com.common.aspect;

import java.lang.reflect.Field;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

import com.common.dao.PersonInfoDAO;
import com.common.dto.PersonInfoDTO;
import com.common.dto.base.BaseDTO;
import com.common.util.ConstantDefine;
import com.common.util.ReflectUtil;
import com.common.util.StringUtil;
import com.common.util.WebUtil;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;


/**
 * 将带空串的字段，设为null； 统一处理BindingResult中的校验错误
 */
@Aspect
public class EmptyStringAndValidControllerAspect {
	private Logger logger = Logger.getLogger(this.getClass());
	
	//@Autowired
	//private PersonInfoDAO personInfoDAO;
	
	// 汉字在数据库中的实际长度
	/*
	 * @Value("${chinese.real.length.in.mysql}") private int realLength;
	 */
	// baseDTO中的属性
	private static String[] createFieldNames = { "creator", "createTime" };
	private static String[] modifyFieldNames = { "modifier", "modifyTime" };
	private static final String id ="id";
	// 匹配com.zsrt.oa.controller包下所有类的、
	// 所有Controller方法及参数类型为BaseDTO的执行作为切入点
	@Around("(execution(* com.common.controller.*.*(..)) && (args(com.common.dto.base.BaseDTO) || args(com.common.dto.base.BaseDTO,org.springframework.validation.BindingResult))) " )
	public Object processParam(ProceedingJoinPoint jp) throws Throwable {
		long begin = System.currentTimeMillis();
		logger.info(jp.getSignature()+"----------request start:"+begin);
		// 获取目标方法原始的调用参数
		Object[] args = jp.getArgs();
		if (args != null && args.length > 0) {
			// 输出日志
			for (Object obj : args) {
				logger.info("Parameter:" + obj);
			}
			try {
				if (args.length > 1 && args[1] instanceof BindingResult) {
					// 统一计算中文长度在数据库中的实际长度
					// checkLength((BindingResult) args[1]);
					// 读取校验错误信息
					Map<String, Object> errorMap = getErrorInfo((BindingResult) args[1]);
					// 存在错误，返回
					if (errorMap != null && !errorMap.isEmpty()) {
						return WebUtil.getResponseEntity(errorMap);
					}
				}
				// 将空串，处理为null
				if (args[0] instanceof BaseDTO) {
					// 修改目标方法的第一个参数，将空串处理为null
					Class claz = args[0].getClass();
					setNullValue(args[0], claz);
					setNullValue(args[0], claz.getSuperclass());
					// 自动填写创建人、修改人、创建时间、修改时间，查询不用设置
					String method = jp.getSignature().getName();
					// 保持日期时间的一致性
					Date now = Calendar.getInstance().getTime();
					// 保存
					if (method.startsWith(ConstantDefine.SAVE)) {
						setCurrentUserAndSystemTime(args[0], createFieldNames,now);
						setCurrentUserAndSystemTime(args[0], modifyFieldNames,now);
						BeanUtils.setProperty(args[0], id, StringUtil.getUUID());
					} // 更新
					if (method.startsWith(ConstantDefine.UPDATE)
							&& !method.equals(ConstantDefine.UPDATE_INIT)) {
						setCurrentUserAndSystemTime(args[0], modifyFieldNames,now);
					}

				}
			} catch (Exception e) {
				logger.info("HandleEmptyStringAspect 处理 异常", e);
				Map<String, Object> result = new HashMap<String, Object>();
				result.put(ConstantDefine.SYS_ERROR_KEY,
						ConstantDefine.SYS_ERR_MSG);
				return result;
			}
		}
		// 没有参数就不会改变原值
		// 以改变后的参数去执行目标方法，并保存目标方法执行后的返回值
		Object result= jp.proceed(args);
		logger.info(jp.getSignature()+"---request end:"+(System.currentTimeMillis()-begin));
		return result;
	}

	private void setNullValue(Object obj, Class claz) throws Exception {
		Field[] fields = claz.getDeclaredFields();
		for (Field f : fields) {
			String type = f.getType().getName();
			// 设置通过反射访问该成员变量时取消访问权限检查
			f.setAccessible(true);
			if ("java.lang.String".equals(type)) {
				String value = (String) f.get(obj);
				if (value != null && "".equals(value.trim())) {
					// 调用setInt()方法为p对象的age成员变量设置值
					f.set(obj, null);
				}
			}
		}
	}

	/**
	 * 自动填写创建人、修改人、创建时间、修改时间（date类型）
	 * 
	 * @param obj
	 * @throws Exception
	 */
	private void setCurrentUserAndSystemTime(Object obj, String[] fieldNames,
			Date now) throws Exception {
		Class superClaz = obj.getClass().getSuperclass();
		for (int i = 0; i < fieldNames.length; i++) {
//			Field field = superClaz.getDeclaredField(fieldNames[i]);
//			field.setAccessible(true);
			Object value = null;
			// 第0个位置是创建人或修改人
			if (i == 0) {
				//String targetPersonCode = (String) WebUtil.getSession(ConstantDefine.PERSONCODE_SESSION);
				//PersonInfoDTO selectByPersonCode = personInfoDAO.selectByPersonCode(targetPersonCode);
				//value = selectByPersonCode.getRealName();
				value = WebUtil.getSession(ConstantDefine.PERSONCODE_SESSION);
			} else {
				value = now;
			}
			BeanUtils.setProperty(obj, fieldNames[i], value);
//			field.set(obj, value);
		}
	}

	private Map<String, Object> getErrorInfo(BindingResult bindingResult) {
		Map<String, Object> errorMap = null;
		if (bindingResult.hasErrors()) {
			errorMap = new HashMap<>();
			// 属性校验错误
			Map<String, Object> fieldErrorMap = new HashMap<String, Object>();
			List<FieldError> fieldErrors = bindingResult.getFieldErrors();
			// bindingResult.getAllErrors()
			for (FieldError error : fieldErrors) {
				fieldErrorMap.put(error.getField(), error.getDefaultMessage());
			}
			errorMap.put(ConstantDefine.FIELD_ERR_MSG, fieldErrorMap);
		}
		return errorMap;
	}
	/**
	 * 统一计算中文长度在数据库中的实际长度
	 * 
	 * @param obj
	 * @param bindingResult
	 * @throws Throwable
	 */
	/*
	 * private void checkLength(BindingResult bindingResult) throws Throwable {
	 * 
	 * getDeclaredFields()获得某个类的所有申明的字段，
	 * 即包括public、private和proteced，但是不包括父类的申明字段。
	 * 
	 * Object obj = bindingResult.getTarget(); Field[] fields =
	 * obj.getClass().getDeclaredFields(); for (Field field : fields) {
	 * field.setAccessible(true); // 如果指定类型的注释存在于此元素上,方法返回true， 否则返回false
	 * boolean hasAnnotation = field.isAnnotationPresent(Length.class); if
	 * (hasAnnotation) { // 如果注解存在于此元素上，此方法返回这个元素的注解指定注释类型，否则返回null Length
	 * annotation = field.getAnnotation(Length.class);// 注释类型 int lengthMax =
	 * annotation.max(); int lengthMin = annotation.min(); // 取得当前属性的值 String
	 * value = (String) field.get(obj); // 计算字符串的总长度 int total =
	 * PatternUtils.chineseLength(value, realLength); //超过最大长度 if (lengthMax <
	 * total) { Integer para[] = {lengthMin,lengthMax}; //设置fieldError
	 * bindingResult.rejectValue(field.getName(),null,para,
	 * "可录入"+(lengthMax/realLength)+"个汉字 或 "+ lengthMax +"个字母");
	 * 
	 * }else if (total < lengthMin) {//不满足最小长度 Integer para[] =
	 * {lengthMin,lengthMax}; //设置fieldError
	 * bindingResult.rejectValue(field.getName(),null,para,
	 * "至少录入"+(lengthMin/realLength)+"个汉字 或 "+ lengthMin +"个字母"); } } } }
	 */
}